#include "StringPtrMap.h"

#include "hash_prime.h"
namespace qb {
namespace base {

StringPtrMap::StringPtrMap(int capcity) {
  capcity = qb::base::GetHashPrimeLargerThan(capcity);
  m_string_buf = new SStringBuf();
  m_hashmap.Build(capcity);
  m_cache.SetUsePool(false);
}

StringPtrMap::~StringPtrMap() {
  // BS_METRICS("");
  Collector collector(&m_cache);
  collector.Enum(m_hashmap);
  m_hashmap.Clear();
  if (m_string_buf) delete m_string_buf;
}

qb::base::StringPtrMap::StringId StringPtrMap::SetAt(const char* name,
                                                     void* ptr) {
  Key key(name, ptr);
  unsigned int hash = key.GetHash();
  Entry* et = m_hashmap.Find(key, hash);
  if (et == 0) {
    key.id = m_string_buf->AddString(name, strlen(name));
    et = NewEntry(key);
    m_hashmap.Insert(et, hash);
    return key.id;
  } else {
    et->key.ptr = ptr;
    return et->key.id;
  }
}

bool StringPtrMap::Lookup(const char* name, void*& ptr) {
  Key key(name, 0);
  unsigned int hash = key.GetHash();
  Entry* et = m_hashmap.Find(key, hash);
  if (et) ptr = (void*)et->key.ptr;
  return et != 0;
}

void StringPtrMap::RemoveAll() {
  // BS_METRICS("");
  m_string_buf->Reset();
  Collector collector(&m_cache);
  collector.Enum(m_hashmap);
  m_hashmap.Clear();
}

void StringPtrMap::ExpandByRatio(double ratio) {
  int count = m_hashmap.GetCount();
  ratio = ratio < 0.4 ? 0.4 : ratio;
  ratio = ratio > 20 ? 20 : ratio;
  int length = (int)((count < 10949 ? 10949 : count) / ratio);
  m_hashmap.Expand(length);
}

void StringPtrMap::InitHashTable(int capcity) {
  // BS_METRICS("InitHashTable:count:%d length:%d
  // cap:%d",m_hashmap.GetCount(),m_hashmap.GetLength(),capcity);
  m_hashmap.Expand(capcity);
}

int StringPtrMap::GetBytes() const {
  int bytes = sizeof(*this);
  bytes += m_string_buf->GetBytes();
  bytes += m_hashmap.GetLength() * sizeof(Entry*);
  bytes += m_hashmap.GetCount() * sizeof(Entry);
  bytes += m_cache.GetCount() * (sizeof(Entry*) * 3 + sizeof(Entry));
  return bytes;
}

qb::base::StringPtrMap::Entry* StringPtrMap::NewEntry(const Key& k) {
  Entry* et = m_cache.Alloc();
  et->hashprev = et->hashnext = 0;
  et->key = k;
  return et;
}

}  // namespace base
}  // namespace qb
